package org.zn.note.java8;

import org.zn.note.java8.Bean.Apple;

import java.util.Comparator;
import java.util.List;

/**
 * Lombok
 * 基本格式：
 * (parameter p1, parameter p2) -> {
 * ...
 * }
 * <p>
 * 函数式接口 @FunctionalInterface ：接口里只有1个方法（默认方法不算）
 * 为什么要设计这样的接口呢？
 * 回想Ch0201中，java的行为参数化，仅仅是将行为封装到对象中，因为java的方法只能接受对象
 * 如果对象中有两个方法，由于lombok是不传方法名的，不知道要调用对象的哪个方法，所以就设计出这样的接口 ---只有一个方法的接口
 * Lombok只能用于函数式接口！ 其实就是一种语法糖。本质上匿名内部类，还是对象的调用。  (Java中一切皆对象！)
 * Lombok实质就是创建了这个接口的匿名类实例，然后逻辑代码就是这个接口唯一方法的实现。
 * <p>
 * 1.8 新加的一些函数式接口（java.util.function）
 * {@link java.util.function.Predicate#test(Object)}  boolean test(T t);
 * 用于条件判断，还有一些操作：and、or、negate、isEqual
 * {@link java.util.function.Consumer#accept(Object)}  void accept(T t);
 * 对传入的Object进行处理，可以链式处理：andThen
 * {@link java.util.function.Function#apply(Object)}   R apply(T t);
 * 带返回值的处理，有两种链式处理：compose加前置处理，andThen加后置处理
 * <p>
 * 另外，为避免自动拆箱装箱，有类似 {@link java.util.function.IntPredicate}
 * <p>
 * 这里也有个不成文的规矩，当2个参数1个返回值，默认用 (T, U) -> R
 * <p>
 * Lombok 调用 内部变量 的规则和匿名内部类是一样的：如果是局部变量，要是final的。
 * 虽然1.7以后吧，不用加final关键字了，但是实质还是final的 --- 不能修改！不然编译报错
 * 为什么？
 * 实例变量在堆上，局部变量在方法栈上。
 * 匿名内部类或Lombok可能在不同的线程访问，方法栈是线程私有，就需要一份副本，所以要求不能更改。
 * <p>
 * Lombok传入方法的语法糖
 * (Apple a) -> a.getWeight()  =  Apple::getWeight
 * () -> Thread.currentThread().dumpStack()  =  Thread.currentThread()::dumpStack
 * (str, i) -> str.substring(i)  =  String::substring
 * (String s) -> System.out.println(s)  =  System.out::println
 * 三种：
 * (args) -> ClassName.staticMethod(args)  =  ClassName::staticMethod        静态方法
 * (arg0, rest) -> arg0.instanceMethod(rest)  =  ClassName::instanceMethod   arg0是ClassName类型的
 * (args) -> expr.instanceMethod(args)  =  expr::instanceMethod              expr是外部实例对象
 * 调用无参构造方法：
 * Supplier<Apple> c1 = () -> new Apple();  =  Supplier<Apple> c1 = Apple::new;
 * Apple a1 = c1.get();
 * 调用由参构造方法：
 * Function<Integer, Apple> c2 = (weight) -> new Apple(weight);  =  Function<Integer, Apple> c2 = Apple::new;
 * <p>
 * <p>
 * ------------------------------------------------------------------------
 * 再理解一下
 * <p>
 * 首先，看下类型判断
 * interface List<E>
 * default void sort(Comparator<? super E> c)
 * interface Comparator<T>
 * int compare(T o1, T o2);
 * 当我List的实例是list<Apple>时，那么sort需要的Comparator实例，就必须是comparator<? super Apple>
 * <p>
 * 1、匿名内部类
 * list.sort(
 * new Comparator<Apple>() {
 *
 * @Override public int compare(Apple a1, Apple a2) {
 * return a1.getWeight().compareTo(a2.getWeight());
 * }
 * }
 * );
 * {@link List#sort(Comparator)} list.sort需要一个Comparator实例，这里用匿名内部类创建的该实例
 * 根据上面的类型判断，这里传入的Comparator实例中泛型是Apple，没问题
 * <p>
 * 2、Lombok
 * list.sort(
 * // 仅需要Comparator.compare方法的实现
 * (a1, a2) -> a1.getWeight().compareTo(a2.getWeight())
 * );
 * 由于{@link Comparator#compare(Object, Object)} 是函数式接口，只有一个抽象方法compare
 * 所以list.sort需要Comparator，一定是调用也仅可能调用这唯一的抽象方法
 * （也可能调Comparator其他的defalut方法，但那些方法有默认实现，不需要具体实现实例）
 * 所以，不需要传入Comparator的实现实例，而仅仅需要传入Comparator.compare方法的实现！
 * （可以这样理解，但其实java一切都是对象，内部还是通过创建对象实例实现的）
 * 根据上面的类型判断，这里的a1、a2肯定是Apple类型或其子类型
 * 那么什么是方法的实现呢，就是入参类型和个数相同，输出类型相同，就可以
 * 而上面的list.sort需要Comparator.compare方法实现，也就是入参是 (Apple a1, Apple a2)，出参是 int 就可以
 * <p>
 * 3、方法引用
 * list.sort(
 * Comparator.comparing(Apple::getWeight)
 * );
 * 三种方法引用的规则
 * (args) -> ClassName.staticMethod(args)  =  ClassName::staticMethod        静态方法
 * (arg0, rest) -> arg0.instanceMethod(rest)  =  ClassName::instanceMethod   arg0是ClassName类型的
 * (args) -> expr.instanceMethod(args)  =  expr::instanceMethod              expr是外部实例对象
 * 这里其实绕的有点晕，没完全绕明白
 */
public class Ch03_Lombok {
    public static void main(String[] args) {
        List<Apple> inventory = Apple.genApples();

        // List.sort(Comparator<? super E> c)

        // 1、实现一个算法并传入
        inventory.sort(new AppleComparator());
        System.out.println(inventory);

        // 2、匿名内部类
        inventory.sort(new Comparator<Apple>() {
            @Override
            public int compare(Apple a1, Apple a2) {
                return a1.getWeight().compareTo(a2.getWeight());
            }
        });

        // 3、Lombok
        inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));

        // 4、使用方法引用
        // 这里用到是方法引用的第一规则
        // 规则一：(args) -> ClassName.staticMethod(args)  =  ClassName::staticMethod        静态方法
        // 规则二：(arg0, rest) -> arg0.instanceMethod(rest)  =  ClassName::instanceMethod   arg0是ClassName类型的
        // 规则三：(args) -> expr.instanceMethod(args)  =  expr::instanceMethod              expr是外部实例对象
        inventory.sort(Comparator.comparing(Apple::getWeight));

        // 5、逆序
        inventory.sort(Comparator.comparing(Apple::getWeight).reversed());

        // 6、比较链：如果两个苹果一样重，则按颜色排序
        inventory.sort(
                Comparator
                        .comparing(Apple::getWeight)
                        .reversed()
                        .thenComparing(Apple::getColor)
        );
    }

    private static class AppleComparator implements Comparator<Apple> {
        public int compare(Apple a1, Apple a2) {
            return a1.getWeight().compareTo(a2.getWeight());
        }
    }
}
